﻿//-----------------------------------------------------------------------
// <copyright file="XunitAssertions.cs" company="Akka.NET Project">
//     Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
//     Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Threading.Tasks;
using Akka.TestKit.Xunit.Internals;
using Xunit;

namespace Akka.TestKit.Xunit;

/// <summary>
/// This class contains several common assert patterns used throughout this testkit.
/// </summary>
public class XunitAssertions : ITestKitAssertions
{
    /// <summary>
    /// Fails an assertion without checking any conditions.
    /// </summary>
    /// <param name="format">A template string to display if the assertion fails.</param>
    /// <param name="args">An optional object array that contains zero or more objects to format.</param>
    public void Fail(string format = "", params object[] args)
    {
        Assert.Fail(AkkaEqualException.BuildAssertionMessage(format, args));
    }

    /// <summary>
    /// Verifies that a specified <paramref name="condition"/> is true.
    /// </summary>
    /// <param name="condition">The condition that is being verified.</param>
    /// <param name="format">A template string to display if the assertion fails.</param>
    /// <param name="args">An optional object array that contains zero or more objects to format.</param>
    public void AssertTrue(bool condition, string format = "", params object[] args)
    {
        Assert.True(condition, AkkaEqualException.BuildAssertionMessage(format, args));
    }

    /// <summary>
    /// Verifies that a specified <paramref name="condition"/> is false.
    /// </summary>
    /// <param name="condition">The condition that is being verified.</param>
    /// <param name="format">A template string to display if the assertion fails.</param>
    /// <param name="args">An optional object array that contains zero or more objects to format.</param>
    public void AssertFalse(bool condition, string format = "", params object[] args)
    {
        Assert.False(condition, AkkaEqualException.BuildAssertionMessage(format, args));
    }

    /// <summary>
    /// Verifies that the two specified values (<paramref name="expected"/> and <paramref name="actual"/>
    /// are equal using the built-in comparison function <see cref="AkkaAssertEqualityComparer{T}"/>.
    /// </summary>
    /// <typeparam name="T">The type that is being compared.</typeparam>
    /// <param name="expected">The expected value of the object</param>
    /// <param name="actual">The actual value of the object</param>
    /// <param name="format">A template string to display if the assertion fails.</param>
    /// <param name="args">An optional object array that contains zero or more objects to format.</param>
    /// <exception cref="AkkaEqualException">
    /// This exception is thrown when the two specified values are not equal.
    /// </exception>
    public void AssertEqual<T>(T expected, T actual, string format = "", params object[] args)
    {
        var comparer = new AkkaAssertEqualityComparer<T>();
        if(!comparer.Equals(expected, actual))
            throw AkkaEqualException.ForMismatchedValues(expected, actual, format, args);
    }

    /// <summary>
    /// Verifies that the two specified values (<paramref name="expected"/> and <paramref name="actual"/>
    /// are equal using a specified comparison function <paramref name="comparer"/>.
    /// </summary>
    /// <typeparam name="T">The type that is being compared.</typeparam>
    /// <param name="expected">The expected value of the object</param>
    /// <param name="actual">The actual value of the object</param>
    /// <param name="comparer">The function used to compare the two specified values.</param>
    /// <param name="format">A template string to display if the assertion fails.</param>
    /// <param name="args">An optional object array that contains zero or more objects to format.</param>
    /// <exception cref="AkkaEqualException">
    /// This exception is thrown when the two specified values are not equal.
    /// </exception>
    public void AssertEqual<T>(T expected, T actual, Func<T, T, bool> comparer, string format = "", params object[] args)
    {
        if(!comparer(expected, actual))
            throw AkkaEqualException.ForMismatchedValues(expected, actual, format, args);
    }

    public Exception AssertThrows(Action action)
    {
        return Assert.ThrowsAny<Exception>(action);
    }
    
    public TException AssertThrows<TException>(Action action) where TException : Exception
    {
        return Assert.ThrowsAny<TException>(action);
    }

    public async Task<Exception> AssertThrowsAsync(Func<Task> action)
    {
        return await Assert.ThrowsAnyAsync<Exception>(action);
    }
    
    public async Task<TException> AssertThrowsAsync<TException>(Func<Task> action) where TException : Exception
    {
        return await Assert.ThrowsAnyAsync<TException>(action);
    }
}